//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __PROCESS_H__
#define __PROCESS_H__

#include <stdlib.h>
#include <_pubcrt.h>

class CSharedMemory;

class _CProcess : public IProcess
{
public:
    CARAPI_(PInterface) Probe(REIID riid);
};

class _CMemoryTrace : public IMemoryTrace
{
public:
    CARAPI_(PInterface) Probe(REIID riid);
};

typedef struct _AttachedMemory : public DLinkNode
{
    CSharedMemory* m_pSharedMem;
} AttachedMemory;

typedef enum ProcessState
{
    ProcessState_Unstarted      = 0x00,
    ProcessState_Starting       = 0x01,
    ProcessState_Running        = 0x02,
    ProcessState_Finished       = 0x03,
} ProcessState;

class EventNode : public DLinkNode
{
public:
    EventNode() : m_event(FALSE, UNSIGNALED) {}
    ~EventNode() {}

public:
    DzEvent   m_event;
};

class CProcess :
    public ObjectEnumeratorNode,
    public _CProcess,
    public _CMemoryTrace
{
// Interface functions
//
public:
    CARAPI_(UInt32) AddRef(void);

    CARAPI_(UInt32) Release(void);

    CARAPI Aggregate(
        /* [in] */ AggregateType type,
        /* [in] */ IInterface* pObject);

    CARAPI GetDomain(
        /* [out] */ IInterface** ppObject);

    CARAPI Start(
        /* [in] */ WString name,
        /* [in] */ WString args);

    CARAPI Kill();

    CARAPI WaitForExit(
        /* [in] */ Millisecond32 timeout,
        /* [out] */ WaitResult *pResult);

    CARAPI IsAlive(
        /* [out] */ Boolean *pAlive);

    CARAPI SetPriorityRank(
        /* [in] */ ThreadPriorityRank priorityRank);

    CARAPI GetPriorityRank(
        /* [out] */ ThreadPriorityRank * pPriorityRank);

    CARAPI SetThreadPoolCapacity(
        /* [in] */ Int32 capacity);

    CARAPI GetThreadPoolState(
        /* [out] */ Int32 * pCapacity,
        /* [out] */ Int32 * pThreads);

    CARAPI GetAllThreads(
        /* [out] */ IObjectEnumerator **ppThreads);

    CARAPI GetAllModules(
        /* [out] */ IObjectEnumerator **ppModules);

    CARAPI GetModuleByName(
        /* [in] */ WString mame,
        /* [out] */ IModule * * ppMod);

    CARAPI GetModuleByAddress(
        /* [in] */ Address address,
        /* [out] */ IModule * * ppMod);

    CARAPI GetId(
        /* [out] */ ProcessId * pPid);

    CARAPI GetStartInfo(
        /* [out] */ WStringBuf * pName,
        /* [out] */ WStringBuf * pArgs);

    CARAPI GetExitCode(
        /* [out] */ ExitCode * pExitCode);

    CARAPI GetStartTime(
        /* [out] */ SystemTime * pTime);

    CARAPI GetExitTime(
        /* [out] */ SystemTime * pTime);

    CARAPI GetProcessorTimes(
        /* [out] */ Int32 * pUserTime,
        /* [out] */ Int32 * pKernelTime);

    CARAPI GetMemorySizes(
        /* [out] */ Int32 * pPrivateVirtualMemorySize,
        /* [out] */ Int32 * pSharedVirtualMemorySize,
        /* [out] */ Int32 * pPrivatePhysicalMemorySize,
        /* [out] */ Int32 * pSharedPhysicalMemorySize);

// IMemoryTrace
    CARAPI SetTraceArgs(
               /* [in]*/ Int32  argId,
               /* [in]*/ Int32  index,
               /* [in]*/ Address watchAddr);
               
    CARAPI SetTraceDebug(
               /* [in]*/ Boolean Debug);

    CARAPI IsTraceDebug(
               /* [out]*/ Boolean *pDebug);

    ECode GetArgsId(
        /* [out] */ wchar_t *pwchArgId);

    ECode GetProIndex(
        /* [out] */ Int32 *pIndex);

    ECode GetWatchAddress(
        /* [out] */ Address *pWatchAddress);

// member functions
//
public:
    CARAPI_(UInt32) Release(Thread *pThread);

    CARAPI_(PInterface) GetObject();

    CARAPI_(PInterface) ObjProbe(
                REIID Self_riid,
                REIID riid);

    void Activate();

    ECode LoadModuleW(
            const wchar_t * wszName,
            Int32 *puStackTop,
            BufferOf<Address> * pDllEntries,
            Int32 dwFlags,
            ModuleFileType  modFileType,
            ModuleView ** ppModuleView,
            Boolean *pIsLoaded);

    ECode CreateModuleView(
            CModule * pModule,
            Int32 *pStackTop,
            BufferOf<Address> * pDllEntries,
            ModuleView ** pModuleView);

    ECode MapModule(
            CModule * pModule);

    ECode RequestSystemEvent(SystemEvents dwInterestedEvents,
        IEvent **ppEvent);

    ModuleView * FindModuleView(const wchar_t * wszName);
    ModuleView * FindModuleView(CModule * pModule);

    ECode PageFault(
        UInt32 uAddr, UInt32 uStackPoint, Boolean bIsKernel,
        Boolean bCheckWritable);

    void UnmapAndDeleteModView(ModuleView *pDelModView);

    void FreeProcess();

    ECode AttachMemory(CSharedMemory * pCSharedMem); // for shared memory
    void DetachMemory(CSharedMemory * pCSharedMem);

    CProcess();
    virtual ~CProcess();

    INLINE ECode GetThread(Thread **ppThread);

    ECode SetBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength)
    {
        assert(GetCurrentProcess() == this);
        return m_hcontext.SetBreakPoint(uNo, type, pvAddress, uLength);
    }

#if defined(_x86)
    ECode SetDebugRegisters(DebugContext *pContext)
    {
        if (NULL == pContext) {
            return E_INVALID_ARGUMENT;
        }
        m_hcontext.SetDebugRegisters(pContext);
        return NOERROR;
    }

    ECode GetDebugRegisters(DebugContext *pContext)
    {
        if (NULL == pContext) {
            return E_INVALID_ARGUMENT;
        }
        m_hcontext.GetDebugRegisters(pContext);
        return NOERROR;
    }
#endif

    ECode CreateEvent(EventNode **ppEventNode);
    ECode DestroyEvent(EventNode *pEventNode);
    ECode NotifyEvent(EventNode *pEventNode);
    ECode WaitEvent(EventNode *pEventNode);

    ECode AcquireThreadLocalSlot(
        /* [in] */ ThreadLocalSlotType slotType,
        /* [out] */ Int32 *pSlotIndex);

    ECode RelinquishThreadLocalSlot(
        /* [in] */ Int32 slotIndex,
        /* [out] */ BufferOf<Address> * pUsedPtrs);

    ECode CleanUpThreadLocalSlot(
        /* [in] */ Int32 slotIndex,
        /* [out] */ BufferOf<Address> * pUsedPtrs);

private:
    Boolean IsValidEventNode(EventNode *pEventNode)
    {
        register EventNode *pTmpNode;
        ForEachDLinkNode(EventNode *, pTmpNode, &m_eventList) {
            if (pTmpNode == pEventNode) {
                return TRUE;
            }
        }

        return FALSE;
    }

// member variable
//
public:
    ProcessState    m_processState;

    ThreadPriorityRank    m_priorityRank;

    AS              m_AS;

    ModuleView      *m_pMainModule;
    DLinkNode       m_moduleList;
    KMutex          m_moduleLock;
    Int32           m_nModuleListState;

    Thread          *m_pMainThread;
    DLinkNode       m_threadList;
    KMutex          m_threadLock;
    Int32           m_nThreadListState;

    DLinkNode       m_importServiceList; //for car service

    DLinkNode       m_attachedMemoryList; // for Shared Memory
    KMutex          m_sharedMemLock;

    ThreadPool      m_threadPool;

    wchar_t         *m_wszName;
    wchar_t         *m_wszArgs;

    ProcessHContext m_hcontext;

    // The follow member are use to delete ModuleView
    // see CProcess::UnmapAndDeleteModView()
    //
    DLinkNode       m_deleteModList;
    KMutex          m_deleteModLock;
    UInt32          m_uStackTop;
    Boolean            m_bIsCallDllDetach;

    UInt32          m_uUserProcessorTicks;
    UInt32          m_uPrivilegedProcessorTicks;
    SystemTime          m_startTime;
    SystemTime          m_finishTime;

    int             m_nExitCode;

    virtaddr_t      m_vaBlock;

    DzEvent         m_startEvent;
    ECode           m_startResult;

    DzEvent         m_exitEvent;

    CMutex          m_envMutex;

    DLinkNode       m_eventList;
    KMutex          m_eventListLock;

    Byte            m_tlsTypes[TLS_TOTAL_SLOTS];

    UInt32          m_DebugHandle;

#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
    int             m_nCSOwnerInfo;
#endif

public:
    static DLinkNode    s_processList;
    static KMutex       s_processListLock;
    static Int32          s_nProcessListState;

private:
	Boolean m_bDebug;

#ifndef _RELEASE
private:
    wchar_t m_wcTraceArgId;
    Int32 m_traceProIndex;
    Address m_pTraceWatchAddress;
//    Boolean m_bDebug;
#endif //_RELEASE
};

INLINE CProcess::CProcess() :
    m_pMainModule(NULL),
#ifdef DEBUG_KMUTEX
    m_moduleLock(__FILE__, __LINE__),
#endif //DEBUG_KMUTEX
    m_pMainThread(NULL),
#ifdef DEBUG_KMUTEX
    m_threadLock(__FILE__, __LINE__),
    m_sharedMemLock(__FILE__, __LINE__),
#endif //DEBUG_KMUTEX
    m_wszName(NULL), m_wszArgs(NULL),
#ifdef DEBUG_KMUTEX
    m_deleteModLock(__FILE__, __LINE__),
#endif //DEBUG_KMUTEX
    m_uStackTop(0), m_bIsCallDllDetach(TRUE), m_vaBlock(NULL),
    m_exitEvent(TRUE),
#ifdef DEBUG_KMUTEX
    m_eventListLock(__FILE__, __LINE__),
#endif //DEBUG_KMUTEX
    m_DebugHandle(PROCESS_SIGNATURE)
{
    m_threadPool.Initialize(this);
    m_moduleList.Initialize();
    m_threadList.Initialize();
    m_deleteModList.Initialize();
    m_importServiceList.Initialize();
    m_attachedMemoryList.Initialize();
    m_hcontext.Initialize();

    m_processState = ProcessState_Unstarted;

    m_priorityRank = ThreadPriorityRank_Normal;

    m_uUserProcessorTicks = 0;
    m_uPrivilegedProcessorTicks = 0;

    m_nExitCode = -1;

    m_envMutex.AddRef();

    m_eventList.Initialize();

    memset(m_tlsTypes, ThreadLocalSlotType_Free, TLS_TOTAL_SLOTS);
    m_tlsTypes[TL_SEQUENCED_SLOT] = ThreadLocalSlotType_Value;
    m_tlsTypes[TL_QUIT_SLOT] = ThreadLocalSlotType_Obj;
    m_tlsTypes[TL_DOMAIN_SLOT] = ThreadLocalSlotType_Value;
    m_tlsTypes[TL_CALLBACK_SLOT] = ThreadLocalSlotType_Obj;
    m_tlsTypes[TL_THREAD_INFO_SLOT] = ThreadLocalSlotType_Obj;
    m_tlsTypes[TL_HELPER_INFO_SLOT] = ThreadLocalSlotType_Value;
    m_tlsTypes[TL_APPLET_SLOT] = ThreadLocalSlotType_Obj;
    m_tlsTypes[TL_ORG_CALLBACK_SLOT] = ThreadLocalSlotType_Obj;
    m_tlsTypes[TL_APPLET_DIR_SLOT] = ThreadLocalSlotType_Value;

    m_bDebug = FALSE;
#ifndef _RELEASE
    m_wcTraceArgId = L'\0';
    m_traceProIndex = 0;
    m_pTraceWatchAddress = 0;
//    m_bDebug = FALSE;
#endif //_RELEASE
}

INLINE CProcess::~CProcess()
{
    if (m_wszName) delete[] m_wszName;
    if (m_wszArgs) delete[] m_wszArgs;
    assert(m_moduleList.IsEmpty());
}

INLINE ECode CProcess::GetThread(Thread **ppThread)
{
    if (ProcessState_Finished == m_processState &&
        this != ::GetCurrentProcess()) {
        return E_PROCESS_ALREADY_EXITED;
    }

    ScheduleClass *pScheduleClass;
    uint_t uSchedulePolicy;
    GetScheduleClass(m_priorityRank, &pScheduleClass, &uSchedulePolicy);

    return m_threadPool.GetThread(
            ppThread, pScheduleClass, uSchedulePolicy);
}

INLINE ECode CProcess::AttachMemory(CSharedMemory * pCSharedMem)
{
    AttachedMemory * pMem;

    pMem = new AttachedMemory;
    if (pMem) {
        pMem->m_pSharedMem = pCSharedMem;
        m_sharedMemLock.Lock();
        m_attachedMemoryList.InsertFirst(pMem);
        m_sharedMemLock.Unlock();
        return NOERROR;
    }
    return E_OUT_OF_MEMORY;
}

INLINE void CProcess::DetachMemory(CSharedMemory * pCSharedMem)
{
    AttachedMemory * pMem;

    m_sharedMemLock.Lock();
    pMem = (AttachedMemory *)m_attachedMemoryList.m_pNext;
    while ((DLinkNode *)pMem != &m_attachedMemoryList) {
        if (pMem->m_pSharedMem == pCSharedMem) {
            pMem->Detach();
            delete pMem;
            break;
        }
        pMem = (AttachedMemory *)pMem->m_pNext;
    }
    m_sharedMemLock.Unlock();
}

INLINE void CProcess::Activate()
{
    m_AS.Activate();
    m_hcontext.Activate();
}

EXTERN ECode GetBootModule(
    const wchar_t *wszName, virtaddr_t *pvaBase, virtaddr_t *pvaLimit);

INLINE ModuleView * CProcess::FindModuleView(const wchar_t *wszName)
{
    ModuleView * pModuleView;
    wchar_t *wszFullPath;
    wchar_t *pPathEnv;
    wchar_t *pPos;
    Boolean bEnd = FALSE;
    int nlen;
	ECode ec;

	ec = E_FILE_NOT_FOUND;
    wszFullPath = new wchar_t[_MAX_PATH];
    if (!wszFullPath) return NULL;
    wszFullPath[0] = L'\0';

	if (!wcsstr(wszName, L"\\") && !wcsstr(wszName,L"/")) {
		ec = GetBootModule(wszName, NULL, NULL);
    	if (ec == NOERROR) {
    		wcscpy(wszFullPath, L"C:/");
    		wcscat(wszFullPath, wszName);
    	}
	}

	if (ec != NOERROR) {
		if (FAILED(WFullPath(wszFullPath, wszName, _MAX_PATH))) {
        	wszFullPath[0] = L'\0';
		}
	}

    if (wszFullPath[0] != L'\0') {
        ForEachDLinkNode(ModuleView *, pModuleView, &m_moduleList) {
            if (!_wcsicmp(wszFullPath, pModuleView->m_pModule->m_wszFullPath)) {
                DzDisablePreemption();
                if (0 == pModuleView->m_cRef) {
                    EnablePreemption();
                    do {
                        DzSleep(1, NULL);
                    } while (ModViewState_Deleted != pModuleView->m_modviewstate);
                    ModuleView *pDelModView = pModuleView;
                    pModuleView = (ModuleView*)pModuleView->Prev();
                    pDelModView->Detach();
                    delete pDelModView;
                }
                else {
                    pModuleView->AddRef();
                    EnablePreemption();
                    delete wszFullPath;
                    return pModuleView;
                }
            }
        }

        if (IS_PATH_SEPARATOR(wszName[0]) || wszName[1] == L':') {
            delete wszFullPath;
            return NULL;
        }
    }

    pPathEnv = WGetEnv(L"PATH");
    if (pPathEnv == NULL) {
        delete wszFullPath;
        return NULL;
    }

    while (!bEnd) {
        if ((pPos = wcschr(pPathEnv, L';')) != NULL) {
            nlen = pPos - pPathEnv;
            nlen = ((nlen < _MAX_PATH) ? nlen : _MAX_PATH);
            wcsncpy(wszFullPath, pPathEnv, nlen);
            wszFullPath[nlen] = L'\0';
            if (*(pPos + 1) != L'\0')
                pPathEnv = pPos + 1;
            else
                bEnd = TRUE;
        }
        else {
            wcsncpy(wszFullPath, pPathEnv, _MAX_PATH);
            wszFullPath[_MAX_PATH] = L'\0';
            bEnd = TRUE;
        }

        if (wcslen(wszFullPath) + wcslen(wszName) + 1 >= _MAX_PATH) continue;
        wcscat(wszFullPath, WCS_PATH_SEPARATOR);
        wcscat(wszFullPath, wszName);

        if (FAILED(WFullPath(wszFullPath, wszFullPath, _MAX_PATH))) continue;
        ForEachDLinkNode(ModuleView *, pModuleView, &m_moduleList) {
            if (!_wcsicmp(wszFullPath, pModuleView->m_pModule->m_wszFullPath)) {
                DzDisablePreemption();
                if (0 == pModuleView->m_cRef) {
                    EnablePreemption();
                    do {
                        DzSleep(1, NULL);
                    } while (ModViewState_Deleted !=
                        pModuleView->m_modviewstate);
                    ModuleView *pDelModView = pModuleView;
                    pModuleView = (ModuleView*)pModuleView->Prev();
                    pDelModView->Detach();
                    delete pDelModView;
                }
                else {
                    pModuleView->AddRef();
                    EnablePreemption();
                    delete wszFullPath;
                    return pModuleView;
                }
            }
        }
    }
    delete wszFullPath;
    return NULL;
}

INLINE ModuleView * CProcess::FindModuleView(CModule *pModule)
{
    ModuleView * pModuleView;

    ForEachDLinkNode(ModuleView *, pModuleView, &m_moduleList) {
        if (pModuleView->m_pModule == pModule) {
            DzDisablePreemption();
            if (0 == pModuleView->m_cRef) {
                EnablePreemption();
                do {
                    DzSleep(1, NULL);
                } while (ModViewState_Deleted != pModuleView->m_modviewstate);
                ModuleView *pDelModView = pModuleView;
                pModuleView = (ModuleView*)pModuleView->Prev();
                pDelModView->Detach();
                delete pDelModView;
            }
            else {
                pModuleView->AddRef();
                EnablePreemption();
                return pModuleView;
            }
        }
    }
    return NULL;
}

INLINE ECode SetBreakPoint(
    UInt32 uNo, BreakPointType type, void *pvAddress, UInt32 uLength)
{
    assert(GetCurrentProcess());
    return GetCurrentProcess()->SetBreakPoint(uNo, type, pvAddress, uLength);
}

// MemoryTrace inline functions

INLINE PInterface _CProcess::Probe(REIID riid)
{
    return (((CProcess *)(_CProcess *)this)-> \
            ObjProbe(EIID_IProcess, riid));
}

INLINE PInterface _CMemoryTrace::Probe(REIID riid)
{
    return (((CProcess *)(_CMemoryTrace *)this)-> \
            ObjProbe(EIID_IMemoryTrace, riid));
}

INLINE ECode CProcess::SetTraceArgs(
    /* [in]*/ Int32 wcArgId,
    /* [in]*/ Int32 index,
    /* [in]*/ Address watchAddress)
{
#ifdef _RELEASE
    return E_NOT_IMPLEMENTED;
#else
    m_wcTraceArgId = wcArgId;
    if (m_wcTraceArgId != L'd' && m_wcTraceArgId != L'v') {
        return E_INVALID_OPTIONS;
    }
    else {
        if (m_wcTraceArgId == L'v') return NOERROR;
    }

    m_traceProIndex = index;
    m_pTraceWatchAddress = watchAddress;
    return NOERROR;
#endif //_RELEASE
}

INLINE ECode CProcess::GetArgsId(
    /* [out] */ wchar_t *pwchArgId)
{
#ifdef _RELEASE
    return E_NOT_IMPLEMENTED;
#else
    if (NULL == pwchArgId) {
        return E_INVALID_ARGUMENT;
    }
    *pwchArgId = m_wcTraceArgId;
    return NOERROR;
#endif //_RELEASE
}

INLINE ECode CProcess::GetProIndex(
    /* [out] */ Int32 *pIndex)
{
#ifdef _RELEASE
    return E_NOT_IMPLEMENTED;
#else
    if (NULL == pIndex) {
        return E_INVALID_ARGUMENT;
    }
    *pIndex = m_traceProIndex;
    return NOERROR;
#endif //_RELEASE
}

INLINE ECode CProcess::GetWatchAddress(
    /* [out] */ Address *pWatchAddress)
{
#ifdef _RELEASE
    return E_NOT_IMPLEMENTED;
#else
    if (NULL == pWatchAddress) {
        return E_INVALID_ARGUMENT;
    }
    *pWatchAddress = m_pTraceWatchAddress;
    return NOERROR;
#endif //_RELEASE
}

INLINE ECode CProcess::SetTraceDebug(
    /* [in] */ Boolean bDebug)
{
//#ifdef _RELEASE
    //return E_NOT_IMPLEMENTED;
//#else
    m_bDebug = bDebug;
    return NOERROR;
//#endif //_RELEASE
}

INLINE ECode CProcess::IsTraceDebug(
    /* [out] */ Boolean *pDebug)
{
//#ifdef _RELEASE
    //return E_NOT_IMPLEMENTED;
//#else
    if (NULL == pDebug) {
        return E_INVALID_ARGUMENT;
    }
    *pDebug = m_bDebug;
    return NOERROR;
//#endif //_RELEASE
}

#endif // __PROCESS_H__
